package com.somewater.game.controllers
{
	import com.adobe.serialization.json.JSON;
	import com.somewater.control.IClear;
	import com.somewater.controller.RpcHandler;
	import com.somewater.storage.GameUserDef;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.net.Socket;
	import flash.utils.Timer;
	import flash.utils.getTimer;

	public class SocketHandler extends EventDispatcher implements IClear
	{
		public static var instance:SocketHandler;
		
		private var socket:Socket;
		private var timer:Timer;
		private var connected:Boolean = false;
		private var serverTimeDelta:Number = 0;
		private var singleListeners:Array = [];
		private var listeners:Array = [];
		
		private var lastWriteTime:uint;
		
		public function SocketHandler()
		{
			
		}
		
		public static function Initialize():void
		{
			instance = new SocketHandler();
		}
		
		
		public function init():void
		{
			clear();
			
			socket = new Socket();
			socket.addEventListener(Event.CONNECT, onConnect);
			socket.addEventListener(Event.CLOSE, onClose);
			socket.addEventListener(IOErrorEvent.IO_ERROR, ioError);
			socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityError);
			socket.addEventListener(ProgressEvent.SOCKET_DATA, onData);

			timer = new Timer(1000);
			timer.addEventListener(TimerEvent.TIMER, onTimer);
			
			socket.connect("62.75.145.88", 30001);
		}
		
		
		public function clear():void
		{
			if(socket)
			{
				socket.removeEventListener(Event.CONNECT, onConnect);
				socket.removeEventListener(Event.CLOSE, onClose);
				socket.removeEventListener(IOErrorEvent.IO_ERROR, ioError);
				socket.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityError);
				socket.removeEventListener(ProgressEvent.SOCKET_DATA, onData);
				if(socket.connected) socket.close();
			}
			
			if(timer)
			{
				timer.removeEventListener(TimerEvent.TIMER, onTimer);
				timer.stop();
				timer = null;
			}
			
			socket = null;
			connected = false;
			singleListeners = [];
			listeners = [];
		}
		
		
		public function auth():void
		{
			write("auth", {user_id: RpcHandler.instance.user_id,
							secret: RpcHandler.instance.auth_key,
							first_name: GameUserDef.instance.first_name});
		}
		
		
		public function action(name:String):void
		{
			write(name);
		}
		
		
		public function ping():void
		{
			write("ping");
		}
		
		
		
		public function write(action:String, param:Object = null):void
		{
			if(param == null)
				param = {};
			
			param["action"] = action;
			
			var request:String = JSON.encode(param);
			request = request.length + "\r\n" + request + "\r\n";
			
			trace("SOCKET: request>\n*** start ***\n" + request + "\n*** end ****");
			
			lastWriteTime = getTimer();
			
			if(socket)
			{
				try
				{
					socket.writeUTFBytes(request);
				}catch(err:Error)
				{
					trace("SOCKET: Error " + err);
				}
			}
		}
		
		
		/**
		 * {.... "filed":"value", ....}
		 * listener({.... "filed":"value", ....});
		 */
		public function addSingleListener(field:String, listener:Function):void
		{
			for(var i:int = 0;i<singleListeners.length;i++)
				if(Listener(singleListeners[i]).callback == listener)
					return;
			
			singleListeners.push(new Listener(listener, field));
		}
		
		
		
		
		/**
		 * {.... "filed":"value", ....}
		 * listener({.... "filed":"value", ....});
		 */
		public function addListener(field:String, listener:Function):void
		{
			for(var i:int = 0;i<listeners.length;i++)
				if(Listener(listeners[i]).callback == listener)
					return;
			
			listeners.push(new Listener(listener, field));
		}
		
		
		
		public function removeListener(listener:Function):void
		{
			for(var i:int = 0;i<listeners.length;i++)
				if(Listener(listeners[i]).callback == listener)
				{
					listeners.splice(i, 1);
					return;
				}
		}
		
		
		
		private function onConnect(e:Event):void
		{
			connected = true;
			trace("SOCKET: **** connect ****");
			
			if(timer && timer.running == false)
			{
				timer.start();
			}
			
			auth();
		}
		
		
		/**
		 * Таймер перилдического пинга
		 */
		private function onTimer(e:Event):void
		{
			if(getTimer() - lastWriteTime > 3000)
			{
				// уже 3 секунды не посылаись запроосы
				ping();
			}
		}
		
		
		
		private function onClose(e:Event):void
		{
			connected = false;
			trace("SOCKET: **** close ****");
		}
		
		
		private function ioError(e:IOErrorEvent):void
		{
			trace("SOCKET: **** io error ****");
		}
		
		private function securityError(e:SecurityErrorEvent):void
		{
			trace("SOCKET: **** security error ****");
		}
		
		
		private function onData(e:ProgressEvent):void
		{
			trace("SOCKET: data...");
			
			var data:String = socket.readUTFBytes(socket.bytesAvailable);
			trace(data);
			
			var obj:Object;
			try
			{
				obj = JSON.decode(data);
			}catch(err:Error)
			{
				trace("SOCKET: json error");
			}
			
			if(obj)
			{
				// LISTENERS
				for(var field:String in obj)
				{
					for(var i:int = 0;i<listeners.length;i++)
					{
						if(Listener(listeners[i]).field == field)
						{
							Listener(listeners[i]).callback(obj);
						}
					}
				}
				
				// SINGLE LISTENERS
				for(var field:String in obj)
				{
					var i:int;
					while(i < singleListeners.length)
					{
						if(Listener(singleListeners[i]).field == field)
						{
							Listener(singleListeners[i]).callback(obj);
							singleListeners.splice(i, 1);
						}else{
							i++;
						}
					}
				}
			}
		}
	}
}

class Listener
{
	public var callback:Function;
	public var field:String;
	
	public function Listener(c:Function, f:String)
	{
		callback = c;
		field = f;
	}
}